#ifndef _GL_VIEW
#define _GL_VIEW



#include <vector>
#include <string>
#include <iostream>
#include <cmath>
#include <fstream>
#include <map> 



namespace math { namespace ui 
{



//const float DEG2RAD = 3.141593f / 180;
const float FOV_Y = 60.0f;
//Initial values to set frustrum
const float INIT_LEFT = -1.0f;
const float INIT_RIGHT = 1.0f;
const float INIT_BOTTOM = -1.0f;
const float INIT_TOP = 1.0f;
const float INIT_NEAR = 0.0001f;
const float INIT_FAR = 100.0f;
//Initial values to set camera
const float CAMERA_AGL_X = 45.0f;     // pitch 
const float CAMERA_AGL_Y = -45.0f;    // heading 
const float CAMERA_DIST = -5.0f;    

enum DISPLAY_MODE {FILL_MODE, FRAME_MODE, POINT_MODE} ;
enum PROJECT_MODE {FRUSTUM_MODE, ORTHO_MODE } ;


class ViewParameter
{
public:
	typedef math::vector<float, 3> point3 ; 
	typedef math::vector<float, 2> point2 ; 
	typedef math::vector<int, 2> point2i ; 
	typedef math::vector<float, 4> color_type ;

ViewParameter(): windowWidth (900), windowHeight(600), windowSizeChanged(true), 
		mouseLeftDown(false), mouseRightDown(false), mouseMiddleDown (false), mouse_winpos(point2i(0, 0)), 
		cameraAglX (CAMERA_AGL_X), cameraAglY(CAMERA_AGL_Y),  cameraX(0.0f), cameraY (0.0f),
		projectionLeft(INIT_LEFT), projectionRight(INIT_RIGHT), 
		projectionBottom(INIT_BOTTOM) , projectionTop(INIT_TOP),
		projectionNear (INIT_NEAR), projectionFar(INIT_FAR), 
		display_mode(FILL_MODE), projectMode (FRUSTUM_MODE)
	{
		focus = point3 (0, 0, CAMERA_DIST) ;
	}

public:
	int windowWidth;
	int windowHeight;
	bool windowSizeChanged;

	bool mouseLeftDown;
	bool mouseRightDown;
	bool mouseMiddleDown ;

//	int mouseX;
//	int mouseY;
	point2i mouse_winpos ;

	float cameraAglX;
	float cameraAglY;
	point3 focus; 

	float cameraX ;
	float cameraY ;

	float projectionLeft;
	float projectionRight;
	float projectionBottom;
	float projectionTop;
	float projectionNear;
	float projectionFar;

//	ViewMatrix<float> view_matrix ;
	color_type  backgroundColor ;
	DISPLAY_MODE display_mode ;//viewMode; 
	PROJECT_MODE projectMode ;

} ;

class View {} ;
class GLView  : public View 
{

public :
	typedef vector<float, 3> point3 ; 
	typedef vector<float, 2> point2 ; 
	typedef vector<float, 4> color_type ;
	typedef vector<int, 2> point2i; 
	typedef Box <float, 2>   rectangle_type ;  // Two dimension box is a rectangle. 
public :
	virtual void paint () {}
	virtual void update () {}


	unsigned get_width () const {return this->m_width ;}
	unsigned get_height () const {return this->m_height ;}
	void set_size (unsigned w, unsigned h) {this ->m_width = w; this->m_height = h ;}
	void set_pos (unsigned x, unsigned y) {this->m_xpos = x ; this->m_ypos = y; }
	bool is_visible () const {return this->m_visible ;}
	void set_focus (bool flag = true ){this -> m_focuson = flag;  }
	bool get_focus () const{return this -> m_focuson ;}
	void set_valid ( bool flag = false) {this->m_isvalid = flag ;}
	bool is_valid () const { return this->m_isvalid ;}

protected :
	unsigned m_width, m_height ;
	unsigned m_xpos, m_ypos ;
	bool m_visible ;
	bool m_isvalid ;
	bool m_focuson ;
	rectangle_type m_region ;    // The rectangle show the display region .

	bool m_mleftdown ;
	bool m_mmiddledown ;
	bool m_mrightdown ;
	


public :

	GLView (Model * model, unsigned ID)  : m_focuson(true), m_isvalid(false)
	{
		set_model(model, ID );
		m_mleftdown = false ;
		m_mmiddledown = false ;
		m_mrightdown = false ;
	}
	virtual ~ GLView () {}
	GLView (unsigned w, unsigned h, unsigned x = 0, unsigned y = 0)  : m_focuson(true), m_isvalid(false)
	{
		set_region(w, h, x, y); 
	}
	bool is_inregion (int x, int y)
	{
		
		return this->m_region.isInside (point2 (x, y)); 
	}
	void set_region (unsigned w, unsigned h, unsigned x , unsigned y )
	{
		this->m_xpos = x; 
		this->m_ypos = y ;
		this->m_width = w ;
		this->m_height = h; 
		point2 minp ( x, y) ;
		point2 maxp ( x + w, y + h);
		m_region = rectangle_type ((minp + maxp ) * 0.5f , (maxp - minp) * 0.5f) ;
	}

	void set_bgcolor (const color_type & c){this->mainView.backgroundColor = c ;} 


	void set_scissor ()
	{
		point2 pmin = this->m_region.getMin(); 
		point2 pmax = this->m_region.getMax(); 
		point2 offset = pmax - pmin ;
		glViewport (pmin.x, pmin.y, offset.x , offset.y);
		glScissor (pmin.x, pmin.y, offset.x , offset.y);
	}
	/** 
	 * configure projection and viewport of sub window
	 */
	void setOrthoViewport(int x, int y, int width, int height, float nearPlane, float farPlane)
	{
		// set perspective viewing frustum
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		//glOrtho(x, width + x, y , y + height, nearPlane, farPlane );
		glOrtho(x, width + x, y , y + height, -1, 1);
		std::cout<<"size "<<x<<' '<<width<<' '<<y<<' '<<height <<std::endl;

		// switch to modelview matrix in order to set scene
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
	}

	/** 
	 * configure projection and viewport of sub window
	 */
	void setViewport(int x, int y, int width, int height, float nearPlane, float farPlane)
	{
		// set perspective viewing frustum
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60.0, (float)(width)/height, nearPlane, farPlane); // FOV, AspectRatio, NearClip, FarClip

		// switch to modelview matrix in order to set scene
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
	}
	virtual void set_shader (Shader * shader) {}
	virtual void setMousePos (int _x, int _y)
	{
		_x -= m_xpos ;
		m_mouse_pos = point2i(_x, _y);
	}
	virtual void setViewMode (DISPLAY_MODE vm = FILL_MODE)
	{
		this->mainView.display_mode = vm ;
		switch (mainView.display_mode)
		{
		case FILL_MODE:
			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
			glEnable(GL_DEPTH_TEST);
			glEnable(GL_CULL_FACE);
			break;
		case FRAME_MODE:
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			glDisable(GL_DEPTH_TEST);
			glDisable(GL_CULL_FACE);
			//glEnable(GL_DEPTH_TEST);
			//glEnable(GL_CULL_FACE);
			break;

		case POINT_MODE:
			glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
			//glDisable(GL_DEPTH_TEST);
			glDisable(GL_CULL_FACE);
			break;
		}
	}

	virtual void setLButtonDown (bool flag = true)
	{
		m_mleftdown = flag ;
	}
	virtual void setRButtonDown (bool flag = true)
	{
		m_mrightdown = flag ;
	}

	virtual void setMButtonDown (bool flag = true)
	{
		m_mmiddledown = flag ;
	}
	virtual void rotateCamera (int x, int y)
	{
	}
	virtual void translateCamera (int x, int y)
	{
	}
	virtual void zoomCamera(int x, int y)
	{
	}
	virtual void select_face (){}
	virtual void select_edge (){}
	virtual void paint_model (){}
	virtual void resize (int w, int h)
	{

	}
	virtual void set_viewmode (DISPLAY_MODE vm = FILL_MODE){}

protected :
	void set_model ( Model * model, unsigned ID) 
	{
		mp_model = model ;
		model->add_control (this, ID);
	}

	ViewParameter mainView ;
	Model * mp_model ;
	point2i m_mouse_pos; 

};

}}

#endif
